/*====================================================================================
# «ДЭНСИ:РМК» module of «ДЭНСИ: КАССА» complex is the front-office application for POS-systems based on fiscal registrators.
# © OOO "DNC Trade" 2009, Authors: Savitskaya Alexandra, Pavlova Irina(Pavlova_Irina@shtrih-s.ru), Sukhanova Anna (anuta.s13@gmail.com), Savrasov Alexandr.
# This is a FREE software, licensed under GNU/GPL terms. See details in LICENSING_ENG.TXT in main directory
#====================================================================================
# Модуль «ДЭНСИ:РМК» программного комплекса «ДЭНСИ: КАССА» - рабочее место кассира для POS-систем на базе фискальных регистраторов
# © ООО "ДЭНСИ Трейд" 2009, Авторы: Савицкая Александра, Павлова Ирина(Pavlova_Irina@shtrih-s.ru), Суханова Анна (anuta.s13@gmail.com), Саврасов Александр.
# Данная программа является свободным программным обеспечением, выпускается на условиях Стандартной Общественной Лицензии GNU/GPL
# Подробнее смотрите в файле LICENSING_RUS.TXT в основной директории
#====================================================================================*/


#include <sys/time.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include "addon_func.h"
#include "recordreceipt.h" 
#include "setup_param.h"

extern SP * sp_receipt;


//==================================================================================//
Record_Receipt::Record_Receipt()
{
//puts("Record_Receipt::Record_Receipt()");
  quantity = 0;
  price = 0;
  min_price = 0;
  sum = 0;
  dsum = 0;
  
  code_flag = false;
  enable_flag = true;
  
  allow_reduction = false;

  flags = 0;
  auto_scheme = 0;
  fix_scheme = 0;
  tax_group = 0;
   
  /* cyberpay options */
  operator_id = -1;
  *session = 0;
  *account = 0;
  *opname = 0;
  *errmsg = 0;
  fee = 0;
  /* end cyberpay options */
}

//==================================================================================//
//Конструктор копирования
Record_Receipt::Record_Receipt(const Record_Receipt & r)
{
  code = r.code;
  barcode = r.barcode;
  shortcut = r.shortcut;
  longtitle = r.longtitle;
  

  auto_scheme = r.auto_scheme;
  fix_scheme = r.fix_scheme;
  tax_group = r.tax_group;
  flags = r.flags; 
  
  quantity = r.quantity;
  price = r.price;
  min_price = r.min_price;
  sum = r.sum;
  dsum = r.dsum;
 
  code_flag = r.code_flag; // true - internal code; false - barcode
  enable_flag = r.enable_flag;

  allow_reduction = r.allow_reduction;

  discount = r.discount;

  /* cyberpay options */
  operator_id = r.operator_id;
  strncpy(session, r.session, 64);
  strncpy(account, r.account, 64);
  strncpy(opname, r.opname, 128);
  strncpy(errmsg, r.errmsg, 255);
  fee = r.fee;
  /* end cyberpay options */
}


//==================================================================================//
Record_Receipt::~Record_Receipt()
{
//puts("Record_Receipt::~Record_Receipt()");

}

//==================================================================================//
Discount * Record_Receipt::GetDiscountElemRef(int index)
{
    
    //puts("Discount * Record_Receipt::GetDiscountElemRef(int index)");
  return &discount.at(index);
}

//==================================================================================//
const char * Record_Receipt::GetBarcode()
{
//puts("const char * Record_Receipt::~GetBarcode()");
  return barcode.c_str();
}

//==================================================================================//
const char * Record_Receipt::GetCode()
{
//puts("const char * Record_Receipt::~GetCode()");
  return code.c_str();
}

//==================================================================================//
const char * Record_Receipt::GetShortcut()
{
//puts("const char * Record_Receipt::~GetShortcut()");
  return shortcut.c_str();
}

//==================================================================================//
const char * Record_Receipt::GetLongtitle()
{
//puts("const char * Record_Receipt::~GetLongtitle()");
  return longtitle.c_str();
}

//==================================================================================//
int Record_Receipt::GetLenghtBarcode()
{
//puts("int Record_Receipt::~GetLenghtBarcode()");
  return barcode.length();
}

//==================================================================================//
int Record_Receipt::GetLenghtCode()
{
//puts("int Record_Receipt::~GetLenghtCode()");
  return code.length();
}

//==================================================================================//
int Record_Receipt::GetLenghtShortcut()
{
//puts("int Record_Receipt::~GetLenghtShortcut()");
  return shortcut.length();
}

//==================================================================================//
int Record_Receipt::GetLenghtLongtitle()
{
//puts("int Record_Receipt::~GetLongtitle()");
  return longtitle.length();
}

//==================================================================================//
void Record_Receipt::Add_Record_Receipt(const char *barcode_str, const char *code_str, const char *shortcut_str, const char *longtitle_str, double quant, double pr, int fls, bool cflag, int a_sch, int f_sch, int t_gr, double m_pr, int op_id, const char *acc, double f)
{
//puts("void Record_Receipt::Add_Record_Receipt(const char *barcode_str, const char *code_str, const char *shortcut_str, const char *longtitle_str, double quant, double pr, int fls, bool cflag, int a_csh, int f_sch, int t_gr, double m_pr)");
  dsum = 0;

  barcode = barcode_str;
  code = code_str;
  shortcut = shortcut_str;
  longtitle = longtitle_str;
 
  quantity = quant;
  price = pr;
  min_price = m_pr;
  sum = Round(pr * quant * 100) / 100;

  code_flag = cflag;
  enable_flag = true;

  allow_reduction = false;

  operator_id = op_id;
  strncpy(account, acc, 64);
  fee = f;

  flags = fls;
  auto_scheme = a_sch;
  fix_scheme = f_sch;
  tax_group = t_gr;
  
  discount.resize(4);

//printf("tax_group %d\n sum %4f\n", tax_group,sum);
//puts("END_OF_Record_Receipt::Add_Record_Receipt(...)");

}

//======================================================================//
int Record_Receipt::Add_Sum_Discount(int i, double s, const char * code, const char *name, const char *shortcut, bool manual_fl, int control_min_price)
// return :
// 1 -- скидка больше максимальной допустимой для произвольной скидки
// 2 -- надбавка больше максимальной допустимой для произвольной надбавки
// 3 -- сумма скидки больше суммы позиции
// 4 -- цена товара со скидкой меньше минимальной цена товара
{
  discount[i].Add_Discount(s , 0);
  dsum = RecalculationDiscounts(sum);

  int ret = 0;
  if(manual_fl && (s > 0) && (Round(sum * sp_receipt->max_position_discount) / 100 < s))   //  проверка максимальной скидки для произвольных скидок
    ret = 1;
  else if(manual_fl && (s < 0) && (Round(sum * sp_receipt->max_position_increase) / 100 < - s)) // проверка максимальной надбавки для произвольных надбавок
    ret = 2;
  else if ( (control_min_price == 1) && (sum - dsum < min_price * quantity) ) // котроль минимальной цены товара
    ret = 3;
  else if ( (control_min_price == 2) && (sum - dsum < min_price * quantity) ) {
      discount[i].Restore_Discount();
      dsum = RecalculationDiscounts(sum);
      
      s = (sum - dsum) - min_price * quantity;
      
      discount[i].Add_Discount(s , 0);
      dsum = RecalculationDiscounts(sum);
  } else if(sum - dsum <= 0) // проверка суммы скидки по сравнению с суммой позиции
    ret = 4;
  

  if(ret != 0) // Востанавливаем предыдущие значения скидки и повторно пересчитываем общую скидку на позицию
  {
    discount[i].Restore_Discount();
    dsum = RecalculationDiscounts(sum);
  }
  else
  {
    discount[i].Set_Names(name, shortcut);
    discount[i].Set_Code(code);
  }
  return ret;
}

//==================================================================================//
int Record_Receipt::Add_Percent_Discount(int i, double p, const char * code, const char *name, const char *shortcut, bool manual_fl, int control_min_price)
// 1 -- скидка больше максимальной допустимой для произвольной скидки
// 2 -- надбавка больше максимальной допустимой для произвольной надбавки
// 4 -- цена товара со скидкой меньше минимальной цена товара
{
  discount[i].Add_Discount(0, p);
  dsum = RecalculationDiscounts(sum);
  
  printf("DSUM C = %f", dsum);

  double s = discount[i].dsum;
 
  int ret = 0;
  if(manual_fl && (s > 0) && (Round(sum * sp_receipt->max_position_discount) / 100 < s))   //  проверка максимальной скидки
    ret = 1;
  else if(manual_fl && (s < 0) && (Round(sum * sp_receipt->max_position_increase) / 100 < - s)) // проверка максимальной надбав 
    ret = 2;
  else if ( (control_min_price == 1 ) && (sum - dsum < min_price * quantity) )
    ret = 4;
  else if ( (control_min_price == 2)  && (sum - dsum < min_price * quantity) ) {
	discount[i].Restore_Discount();
	dsum = RecalculationDiscounts(sum);

	// процент
	p = 100 - (min_price * quantity / (sum - dsum)) * 100;

	discount[i].Add_Discount(0, p);
	dsum = RecalculationDiscounts(sum);
  }
  
  if(ret != 0)  // Востанавливаем предыдущие значения скидки и повторно пересчитываем общую скидку на позицию
  {
    discount[i].Restore_Discount();
    dsum = RecalculationDiscounts(sum);
  }
  else
  {
    discount[i].Set_Names(name, shortcut);
    discount[i].Set_Code(code);
  }

  return ret;
}

//==================================================================================//
double Record_Receipt::RecalculationDiscounts(double s)
{
    double dsum = 0;
    switch ( sp_receipt->result_discount ) { // Правило выбора начисления итоговой скидки
    case 0 : // выбирается максимальная скидка и максимальная надбавка
	dsum = MaxDiscountAndCharge(s, discount);
	break;
    case 1 : // выбирается объединение скидок (скидки начисляются последовательно, используя порядок начисления)
	//puts("UNION");
	dsum = Union_Discount(s, discount);
	break;
    case 2 : // скидки суммируются, а потом начисляются
	//puts("SUM");
	dsum = Sum_Discount(s, discount);	
	break;
    }
    
    printf("DSUM A = %f", dsum);

    if ( sp_receipt->round_check_sum && ((sum-dsum) > sp_receipt->round_power) )
    {
    	double round_power = sp_receipt->round_power;

    	int count = 0;
    	//enum RoundType { LESS_ROUND = 0, GREATER_ROUND = 1, BETTER_ROUND = 2 };
    	if ( sp_receipt->round_type == 2 ) count = static_cast< int >(Round((sum-dsum)/round_power));
    	if ( sp_receipt->round_type == 0 )  count = static_cast< int >(floor((sum-dsum)/round_power));
    	if ( sp_receipt->round_type == 1 ) count = static_cast< int >(ceil((sum-dsum)/round_power));

    	dsum = sum - (round_power * count);
    }

    printf("DSUM B = %f", dsum);

    return dsum;
}

double Record_Receipt::RecalculationDiscounts()
{
	return dsum = RecalculationDiscounts(sum);
}

//==================================================================================//

void Record_Receipt::SetPrice(double p)
{
	price = p;
	sum = Round(price * quantity * 100) / 100;
	dsum = RecalculationDiscounts(sum);
}

double Record_Receipt::GetPrice()
{
	return price;
}

void Record_Receipt::SetQuantity(double q)
{
	quantity = q;
	sum = Round(price * quantity * 100) / 100;
	dsum = RecalculationDiscounts(sum);
}

double Record_Receipt::GetQuantity()
{
	return quantity;
}

double Record_Receipt::GetSum()
{
	return sum;
}

double Record_Receipt::GetDSum()
{
	return dsum;
}
